νλ‘ νΈμλ κ°λ°μμ MediaStream νΈλμ 볡μ‘μ±μ νꡬνκ³ , κ°λ ₯ν λ―Έλμ΄ μ ν리μΌμ΄μ ꡬμΆμ μν μμ±, μ‘°μ, μ μ½ μ‘°κ±΄ λ° κ³ κΈ κΈ°μ μ λ€λ£Ήλλ€.
νλ‘ νΈμλ MediaStream νΈλ: λ―Έλμ΄ νΈλ κ΄λ¦¬λ₯Ό μν μ’ ν© κ°μ΄λ
MediaStreamTrack μΈν°νμ΄μ€λ MediaStream λ΄μ λ¨μΌ λ―Έλμ΄ νΈλμ λνλ
λλ€. μ΄ νΈλμ μ€λμ€ λλ λΉλμ€λ₯Ό ν¬ν¨ν μ μμ΅λλ€. μ΄λ¬ν νΈλμ κ΄λ¦¬νλ λ°©λ²μ μ΄ν΄νλ κ²μ μΉμμ κ°λ ₯νκ³ μνΈμμ©μ μΈ λ―Έλμ΄ μ ν리μΌμ΄μ
μ ꡬμΆνλ λ° λ§€μ° μ€μν©λλ€. μ΄ μ’
ν© κ°μ΄λλ νλ‘ νΈμλ κ°λ°μμ MediaStream νΈλμ μμ±, μ‘°μ λ° κ΄λ¦¬μ λν΄ μλ΄ν©λλ€.
MediaStream νΈλμ΄λ 무μμΈκ°?
MediaStreamμ μ¬λ¬ MediaStreamTrack κ°μ²΄λ₯Ό ν¬ν¨ν μ μλ λ―Έλμ΄ μ½ν
μΈ μ μ€νΈλ¦Όμ
λλ€. κ° νΈλμ λ¨μΌ μ€λμ€ λλ λΉλμ€ μμ€λ₯Ό λνλ
λλ€. μ€λμ€λ λΉλμ€ λ°μ΄ν° μ€ νλμ μ€νΈλ¦Όμ λ΄κ³ μλ 컨ν
μ΄λλΌκ³ μκ°νλ©΄ λ©λλ€.
μ£Όμ μμ± λ° λ©μλ
kind: νΈλμ μ ν("audio"λλ"video")μ λνλ΄λ μ½κΈ° μ μ© λ¬Έμμ΄μ λλ€.id: νΈλμ κ³ μ μλ³μλ₯Ό λνλ΄λ μ½κΈ° μ μ© λ¬Έμμ΄μ λλ€.label: μ¬λμ΄ μ½μ μ μλ νΈλμ λ μ΄λΈμ μ 곡νλ μ½κΈ° μ μ© λ¬Έμμ΄μ λλ€.enabled: νΈλμ΄ νμ¬ νμ±νλμ΄ μλμ§ μ¬λΆλ₯Ό λνλ΄λ λΆλ¦¬μΈ κ°μ λλ€. μ΄ κ°μfalseλ‘ μ€μ νλ©΄ νΈλμ μ€μ§νμ§ μκ³ μμκ±°νκ±°λ λΉνμ±νν©λλ€.muted: μμ€ν μμ€ μ μ½μ΄λ μ¬μ©μ μ€μ μΌλ‘ μΈν΄ νΈλμ΄ μμκ±°λμλμ§ μ¬λΆλ₯Ό λνλ΄λ μ½κΈ° μ μ© λΆλ¦¬μΈ κ°μ λλ€.readyState: νΈλμ νμ¬ μν("live","ended")λ₯Ό λνλ΄λ μ½κΈ° μ μ© λ¬Έμμ΄μ λλ€.getSettings(): νΈλμ νμ¬ μ€μ μ¬μ μ λ°νν©λλ€.getConstraints(): νΈλμ΄ μμ±λ λ μ μ©λ μ μ½ μ‘°κ±΄ μ¬μ μ λ°νν©λλ€.applyConstraints(constraints): νΈλμ μλ‘μ΄ μ μ½ μ‘°κ±΄μ μ μ©νλ €κ³ μλν©λλ€.clone(): μλ³Έμ 볡μ λ³ΈμΈ μλ‘μ΄MediaStreamTrackκ°μ²΄λ₯Ό λ°νν©λλ€.stop(): νΈλμ μ€μ§νμ¬ λ―Έλμ΄ λ°μ΄ν°μ νλ¦μ μ’ λ£ν©λλ€.addEventListener():endedλλmuteμ κ°μ νΈλμ μ΄λ²€νΈλ₯Ό μμ ν μ μκ² ν©λλ€.
MediaStream νΈλ μ»κΈ°
MediaStreamTrack κ°μ²΄λ₯Ό μ»λ μ£Όμ λ°©λ²μ getUserMedia() APIλ₯Ό ν΅νλ κ²μ
λλ€. μ΄ APIλ μ¬μ©μμκ² μΉ΄λ©λΌμ λ§μ΄ν¬ μ κ·Ό κΆνμ μμ²νκ³ , κΆνμ΄ λΆμ¬λλ©΄ μμ²λ νΈλμ ν¬ν¨νλ MediaStreamμ λ°νν©λλ€.
getUserMedia() μ¬μ©νκΈ°
λ€μμ getUserMedia()λ₯Ό μ¬μ©νμ¬ μ¬μ©μμ μΉ΄λ©λΌμ λ§μ΄ν¬μ μ κ·Όνλ κΈ°λ³Έ μμ μ
λλ€:
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(function(stream) {
// μ€νΈλ¦Όμ μ¬κΈ°μ μ¬μ©ν©λλ€.
const videoTrack = stream.getVideoTracks()[0];
const audioTrack = stream.getAudioTracks()[0];
// μμ : λΉλμ€ μμμ λΉλμ€ νμνκΈ°
const videoElement = document.getElementById('myVideo');
videoElement.srcObject = stream;
videoElement.play();
})
.catch(function(err) {
console.log("μ€λ₯κ° λ°μνμ΅λλ€: " + err);
});
μ€λͺ :
navigator.mediaDevices.getUserMedia({ video: true, audio: true }): λΉλμ€μ μ€λμ€ μ λ ₯ λͺ¨λμ λν μ κ·Όμ μμ²ν©λλ€.getUserMediaμ μ λ¬λ κ°μ²΄λ μμ²λ λ―Έλμ΄ μ νμ μ μν©λλ€..then(function(stream) { ... }): μ¬μ©μκ° κΆνμ λΆμ¬νκ³MediaStreamμ΄ μ±κ³΅μ μΌλ‘ μ»μ΄μ‘μ λ μ€νλ©λλ€.stream.getVideoTracks()[0]: μ€νΈλ¦Όμμ 첫 λ²μ§Έ λΉλμ€ νΈλμ κ°μ Έμ΅λλ€. μ€νΈλ¦Όμ κ°μ μ νμ μ¬λ¬ νΈλμ ν¬ν¨ν μ μμ΅λλ€.stream.getAudioTracks()[0]: μ€νΈλ¦Όμμ 첫 λ²μ§Έ μ€λμ€ νΈλμ κ°μ Έμ΅λλ€.videoElement.srcObject = stream: λΉλμ€ μμμsrcObjectλ₯ΌMediaStreamμΌλ‘ μ€μ νμ¬ λΉλμ€λ₯Ό νμν μ μκ² ν©λλ€.videoElement.play(): λΉλμ€ μ¬μμ μμν©λλ€..catch(function(err) { ... }): μ¬μ©μκ° κΆνμ κ±°λΆνλ λ± μ€λ₯κ° λ°μνμ λ μ€νλ©λλ€.
μ μ½ μ‘°κ±΄(Constraints)
μ μ½ μ‘°κ±΄μ μ¬μ©νλ©΄ ν΄μλ, νλ μ μλ, μ€λμ€ νμ§κ³Ό κ°μ λ―Έλμ΄ νΈλμ λν μꡬ μ¬νμ μ§μ ν μ μμ΅λλ€. μ΄λ μ ν리μΌμ΄μ
μ΄ νΉμ μꡬμ λ§λ λ―Έλμ΄ λ°μ΄ν°λ₯Ό μμ νλλ‘ λ³΄μ₯νλ λ° μ€μν©λλ€. μ μ½ μ‘°κ±΄μ getUserMedia() νΈμΆ λ΄μμ μ§μ ν μ μμ΅λλ€.
navigator.mediaDevices.getUserMedia({
video: {
width: { min: 640, ideal: 1280, max: 1920 },
height: { min: 480, ideal: 720, max: 1080 },
frameRate: { ideal: 30, max: 60 }
},
audio: {
echoCancellation: { exact: true },
noiseSuppression: { exact: true }
}
})
.then(function(stream) {
// ...
})
.catch(function(err) {
console.log("μ€λ₯κ° λ°μνμ΅λλ€: " + err);
});
μ€λͺ :
width: { min: 640, ideal: 1280, max: 1920 }: λΉλμ€ λλΉκ° μ΅μ 640ν½μ , μ΄μμ μΌλ‘λ 1280ν½μ , μ΅λ 1920ν½μ μ΄μ΄μΌ ν¨μ μ§μ ν©λλ€. λΈλΌμ°μ λ μ΄λ¬ν μ μ½ μ‘°κ±΄μ μ§μνλ μΉ΄λ©λΌλ₯Ό μ°ΎμΌλ €κ³ μλν©λλ€.height: { min: 480, ideal: 720, max: 1080 }: λλΉμ μ μ¬νκ², μνλ λΉλμ€μ λμ΄λ₯Ό μ μν©λλ€.frameRate: { ideal: 30, max: 60 }: μ΄μμ μΌλ‘ μ΄λΉ 30νλ μ, μ΅λ μ΄λΉ 60νλ μμ νλ μ μλλ₯Ό μμ²ν©λλ€.echoCancellation: { exact: true }: μ€λμ€ νΈλμ λν΄ μμ½ μΊμ¬λ μ΄μ μ΄ νμ±νλλλ‘ μμ²ν©λλ€.exact: trueλ λΈλΌμ°μ κ° μμ½ μΊμ¬λ μ΄μ μ *λ°λμ* μ 곡ν΄μΌ νλ©°, κ·Έλ μ§ μμΌλ©΄ μμ²μ΄ μ€ν¨νλ€λ κ²μ μλ―Έν©λλ€.noiseSuppression: { exact: true }: μ€λμ€ νΈλμ λν΄ λ Έμ΄μ¦ μ΅μ κ° νμ±νλλλ‘ μμ²ν©λλ€.
λΈλΌμ°μ κ° λͺ¨λ μ μ½ μ‘°κ±΄μ μΆ©μ‘±μν€μ§ λͺ»ν μλ μλ€λ μ μ μ μνλ κ²μ΄ μ€μν©λλ€. MediaStreamTrackμμ getSettings()λ₯Ό μ¬μ©νμ¬ μ€μ λ‘ μ μ©λ μ€μ μ νμΈν μ μμ΅λλ€.
MediaStream νΈλ μ‘°μνκΈ°
MediaStreamTrackμ μ»μ νμλ λ€μν λ°©λ²μΌλ‘ μ‘°μνμ¬ μ€λμ€ λ° λΉλμ€ μΆλ ₯μ μ μ΄ν μ μμ΅λλ€.
νΈλ νμ±ν λ° λΉνμ±ν
enabled μμ±μ μ¬μ©νμ¬ νΈλμ νμ±ννκ±°λ λΉνμ±νν μ μμ΅λλ€. enabledλ₯Ό falseλ‘ μ€μ νλ©΄ μ€λμ€ νΈλμ ν¨κ³Όμ μΌλ‘ μμκ±°νκ±°λ λΉλμ€ νΈλμ μ€μ§νμ§ μκ³ λΉνμ±νν©λλ€. λ€μ trueλ‘ μ€μ νλ©΄ νΈλμ΄ λ€μ νμ±νλ©λλ€.
const videoTrack = stream.getVideoTracks()[0];
// λΉλμ€ νΈλ λΉνμ±ν
videoTrack.enabled = false;
// λΉλμ€ νΈλ νμ±ν
videoTrack.enabled = true;
μ΄λ μμκ±° λ²νΌμ΄λ λΉλμ€ ν κΈκ³Ό κ°μ κΈ°λ₯μ ꡬννλ λ° μ μ©ν©λλ€.
μμ± ν μ μ½ μ‘°κ±΄ μ μ©νκΈ°
applyConstraints() λ©μλλ₯Ό μ¬μ©νμ¬ νΈλμ΄ μμ±λ νμ νΈλμ μ μ½ μ‘°κ±΄μ μμ ν μ μμ΅λλ€. μ΄λ₯Ό ν΅ν΄ μ¬μ©μ κΈ°λ³Έ μ€μ μ΄λ λ€νΈμν¬ μ‘°κ±΄μ λ°λΌ μ€λμ€ λ° λΉλμ€ μ€μ μ λμ μΌλ‘ μ‘°μ ν μ μμ΅λλ€. κ·Έλ¬λ μΌλΆ μ μ½ μ‘°κ±΄μ νΈλμ΄ μμ±λ νμλ μμ ν μ μμ μλ μμ΅λλ€. applyConstraints()μ μ±κ³΅ μ¬λΆλ κΈ°λ³Έ νλμ¨μ΄μ κΈ°λ₯κ³Ό νΈλμ νμ¬ μνμ λ°λΌ λ¬λΌμ§λλ€.
const videoTrack = stream.getVideoTracks()[0];
videoTrack.applyConstraints({ frameRate: { ideal: 24 } })
.then(function() {
console.log("μ μ½ μ‘°κ±΄μ΄ μ±κ³΅μ μΌλ‘ μ μ©λμμ΅λλ€.");
})
.catch(function(err) {
console.log("μ μ½ μ‘°κ±΄ μ μ©μ μ€ν¨νμ΅λλ€: " + err);
});
νΈλ μ€μ§νκΈ°
νΈλμ μμ ν μ€μ§νκ³ κΈ°λ³Έ 리μμ€λ₯Ό ν΄μ νλ €λ©΄ stop() λ©μλλ₯Ό μ¬μ©ν μ μμ΅λλ€. μ΄λ νΉν λͺ¨λ°μΌ μ₯μΉμ κ°μ΄ 리μμ€κ° μ νλ νκ²½μμ λ μ΄μ νμνμ§ μμ λ μΉ΄λ©λΌμ λ§μ΄ν¬λ₯Ό ν΄μ νλ λ° μ€μν©λλ€. μΌλ¨ νΈλμ΄ μ€μ§λλ©΄ λ€μ μμν μ μμ΅λλ€. getUserMedia()λ₯Ό μ¬μ©νμ¬ μ νΈλμ μ»μ΄μΌ ν©λλ€.
const videoTrack = stream.getVideoTracks()[0];
// νΈλ μ€μ§
videoTrack.stop();
μμ
μ΄ λλλ©΄ μ 체 MediaStreamμ μ€μ§νλ κ²λ μ’μ μ΅κ΄μ
λλ€:
stream.getTracks().forEach(track => track.stop());
κ³ κΈ κΈ°μ
κΈ°λ³Έμ μΈ λ΄μ© μΈμλ MediaStreamTrack κ°μ²΄λ₯Ό μΆκ°λ‘ μ‘°μνκ³ ν₯μμν€κΈ° μν΄ μ¬μ©ν μ μλ μ¬λ¬ κ³ κΈ κΈ°μ μ΄ μμ΅λλ€.
νΈλ 볡μ νκΈ°
clone() λ©μλλ μλ³Έμ μ¬λ³ΈμΈ μλ‘μ΄ MediaStreamTrack κ°μ²΄λ₯Ό μμ±ν©λλ€. 볡μ λ νΈλμ λμΌν κΈ°λ³Έ λ―Έλμ΄ μμ€λ₯Ό 곡μ ν©λλ€. μ΄λ λμΌν λΉλμ€λ₯Ό μ¬λ¬ λΉλμ€ μμμ νμνλ κ²κ³Ό κ°μ΄ λμΌν λ―Έλμ΄ μμ€λ₯Ό μ¬λ¬ κ³³μμ μ¬μ©ν΄μΌ ν λ μ μ©ν©λλ€.
const originalTrack = stream.getVideoTracks()[0];
const clonedTrack = originalTrack.clone();
// 볡μ λ νΈλμΌλ‘ μ MediaStream μμ±
const clonedStream = new MediaStream([clonedTrack]);
// λ€λ₯Έ λΉλμ€ μμμ 볡μ λ μ€νΈλ¦Ό νμ
const videoElement2 = document.getElementById('myVideo2');
videoElement2.srcObject = clonedStream;
videoElement2.play();
μλ³Έ νΈλμ μ€μ§νλ©΄ 볡μ λ νΈλλ μ€μ§λλ€λ μ μ μ μνμΈμ. λμ λμΌν κΈ°λ³Έ λ―Έλμ΄ μμ€λ₯Ό 곡μ νκΈ° λλ¬Έμ λλ€.
μ€λμ€ λ° λΉλμ€ μ²λ¦¬
MediaStreamTrack μΈν°νμ΄μ€ μ체λ μ€λμ€λ λΉλμ€ λ°μ΄ν°λ₯Ό μ§μ μ²λ¦¬νλ λ©μλλ₯Ό μ 곡νμ§ μμ΅λλ€. κ·Έλ¬λ Web Audio API λ° Canvas APIμ κ°μ λ€λ₯Έ μΉ APIλ₯Ό μ¬μ©νμ¬ μ΄λ₯Ό λ¬μ±ν μ μμ΅λλ€.
Web Audio APIλ‘ μ€λμ€ μ²λ¦¬νκΈ°
Web Audio APIλ₯Ό μ¬μ©νμ¬ MediaStreamTrackμ μ€λμ€ λ°μ΄ν°λ₯Ό λΆμνκ³ μ‘°μν μ μμ΅λλ€. μ΄λ₯Ό ν΅ν΄ μ€λμ€ μκ°ν, λ
Έμ΄μ¦ κ°μ, μ€λμ€ ν¨κ³Όμ κ°μ μμ
μ μνν μ μμ΅λλ€.
const audioContext = new AudioContext();
const source = audioContext.createMediaStreamSource(stream);
// μ€λμ€ λ°μ΄ν°λ₯Ό μΆμΆνκΈ° μν΄ λΆμκΈ° λ
Έλ μμ±
const analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
// μμ€λ₯Ό λΆμκΈ°μ μ°κ²°
source.connect(analyser);
analyser.connect(audioContext.destination);
function draw() {
requestAnimationFrame(draw);
// μ£Όνμ λ°μ΄ν° κ°μ Έμ€κΈ°
analyser.getByteFrequencyData(dataArray);
// dataArrayλ₯Ό μ¬μ©νμ¬ μ€λμ€ μκ°ν
// (μ: μΊλ²μ€μ μ£Όνμ μ€ννΈλΌ 그리기)
console.log(dataArray);
}
draw();
μ€λͺ :
new AudioContext(): μλ‘μ΄ Web Audio API 컨ν μ€νΈλ₯Ό μμ±ν©λλ€.audioContext.createMediaStreamSource(stream):MediaStreamμμ μ€λμ€ μμ€ λ Έλλ₯Ό μμ±ν©λλ€.audioContext.createAnalyser(): μ€λμ€ λ°μ΄ν°λ₯Ό μΆμΆνλ λ° μ¬μ©ν μ μλ λΆμκΈ° λ Έλλ₯Ό μμ±ν©λλ€.analyser.fftSize = 2048: μ£Όνμ ꡬκ°μ μλ₯Ό κ²°μ νλ κ³ μ νΈλ¦¬μ λ³ν(FFT) ν¬κΈ°λ₯Ό μ€μ ν©λλ€.analyser.getByteFrequencyData(dataArray):dataArrayλ₯Ό μ£Όνμ λ°μ΄ν°λ‘ μ±μλλ€.draw()ν¨μλrequestAnimationFrame()μ μ¬μ©νμ¬ λ°λ³΅μ μΌλ‘ νΈμΆλμ΄ μ€λμ€ μκ°νλ₯Ό μ§μμ μΌλ‘ μ λ°μ΄νΈν©λλ€.
Canvas APIλ‘ λΉλμ€ μ²λ¦¬νκΈ°
Canvas APIλ₯Ό μ¬μ©νμ¬ MediaStreamTrackμ λΉλμ€ νλ μμ μ‘°μν μ μμ΅λλ€. μ΄λ₯Ό ν΅ν΄ νν° μ μ©, μ€λ²λ μ΄ μΆκ°, μ€μκ° λΉλμ€ λΆμκ³Ό κ°μ μμ
μ μνν μ μμ΅λλ€.
const videoElement = document.getElementById('myVideo');
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
function drawFrame() {
requestAnimationFrame(drawFrame);
// νμ¬ λΉλμ€ νλ μμ μΊλ²μ€μ 그리기
ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
// μΊλ²μ€ λ°μ΄ν° μ‘°μ (μ: νν° μ μ©)
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
// κ°λ¨ν νλ°± νν° μ μ©
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg; // red
data[i + 1] = avg; // green
data[i + 2] = avg; // blue
}
// μμ λ λ°μ΄ν°λ₯Ό λ€μ μΊλ²μ€μ λ£κΈ°
ctx.putImageData(imageData, 0, 0);
}
videoElement.addEventListener('play', drawFrame);
μ€λͺ :
drawFrame()ν¨μλrequestAnimationFrame()μ μ¬μ©νμ¬ λ°λ³΅μ μΌλ‘ νΈμΆλμ΄ μΊλ²μ€λ₯Ό μ§μμ μΌλ‘ μ λ°μ΄νΈν©λλ€.ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height): νμ¬ λΉλμ€ νλ μμ μΊλ²μ€μ 그립λλ€.ctx.getImageData(0, 0, canvas.width, canvas.height): μΊλ²μ€μμ μ΄λ―Έμ§ λ°μ΄ν°λ₯Ό κ°μ Έμ΅λλ€.- μ½λλ ν½μ λ°μ΄ν°λ₯Ό λ°λ³΅νλ©° νλ°± νν°λ₯Ό μ μ©ν©λλ€.
ctx.putImageData(imageData, 0, 0): μμ λ μ΄λ―Έμ§ λ°μ΄ν°λ₯Ό λ€μ μΊλ²μ€μ λ£μ΅λλ€.
WebRTCμ ν¨κ» MediaStream νΈλ μ¬μ©νκΈ°
MediaStreamTrack κ°μ²΄λ λΈλΌμ°μ κ°μ μ€μκ° μ€λμ€ λ° λΉλμ€ ν΅μ μ κ°λ₯νκ² νλ WebRTC(Web Real-Time Communication)μ κΈ°λ³Έμ
λλ€. MediaStreamTrack κ°μ²΄λ₯Ό WebRTC RTCPeerConnectionμ μΆκ°νμ¬ μ격 νΌμ΄μκ² μ€λμ€ λ° λΉλμ€ λ°μ΄ν°λ₯Ό λ³΄λΌ μ μμ΅λλ€.
const peerConnection = new RTCPeerConnection();
// μ€λμ€ λ° λΉλμ€ νΈλμ νΌμ΄ μ°κ²°μ μΆκ°
stream.getTracks().forEach(track => {
peerConnection.addTrack(track, stream);
});
// λλ¨Έμ§ WebRTC μκ·Έλλ§ λ° μ°κ²° μ€μ νλ‘μΈμ€κ° μ¬κΈ°μ μ΄μ΄μ§λλ€.
μ΄λ₯Ό ν΅ν΄ νμ νμ μ ν리μΌμ΄μ , λΌμ΄λΈ μ€νΈλ¦¬λ° νλ«νΌ λ° κΈ°ν μ€μκ° ν΅μ λꡬλ₯Ό ꡬμΆν μ μμ΅λλ€.
λΈλΌμ°μ νΈνμ±
MediaStreamTrack APIλ Chrome, Firefox, Safari, Edgeλ₯Ό ν¬ν¨ν νλ λΈλΌμ°μ μμ λ리 μ§μλ©λλ€. κ·Έλ¬λ MDN μΉ λ¬Έμμ κ°μ 리μμ€μμ μ΅μ λΈλΌμ°μ νΈνμ± μ 보λ₯Ό νμΈνλ κ²μ΄ νμ μ’μ μκ°μ
λλ€.
λͺ¨λ² μ¬λ‘
- κΆν μ μ€νκ² μ²λ¦¬νκΈ°: νμ μΉ΄λ©λΌ λ° λ§μ΄ν¬ μ κ·Όμ λν μ¬μ©μ κΆνμ μ μ€νκ² μ²λ¦¬νμΈμ. μ ν리μΌμ΄μ μ΄ μ μ΄λ¬ν μ₯μΉμ μ κ·Όν΄μΌ νλμ§ λͺ ννκ² μ€λͺ ν΄μΌ ν©λλ€.
- νμ μμ λ νΈλ μ€μ§νκΈ°: λ μ΄μ μ¬μ©νμ§ μμ λλ νΈλμ μ€μ§νμ¬ μΉ΄λ©λΌμ λ§μ΄ν¬ 리μμ€λ₯Ό ν΄μ νμΈμ.
- μ μ½ μ‘°κ±΄ μ΅μ ννκΈ°: μ μ½ μ‘°κ±΄μ μ¬μ©νμ¬ μ ν리μΌμ΄μ μ μ΅μ μ λ―Έλμ΄ μ€μ μ μμ²νμΈμ. νμνμ§ μμ κ²½μ° μ§λμΉκ² λμ ν΄μλλ νλ μ μλλ₯Ό μμ²νμ§ λ§μΈμ.
- νΈλ μν λͺ¨λν°λ§νκΈ°:
endedλ°muteμ κ°μ μ΄λ²€νΈλ₯Ό μμ νμ¬ νΈλ μνμ λ³νμ λμνμΈμ. - λ€μν μ₯μΉμμ ν μ€νΈνκΈ°: νΈνμ±μ 보μ₯νκΈ° μν΄ λ€μν μ₯μΉμ λΈλΌμ°μ μμ μ ν리μΌμ΄μ μ ν μ€νΈνμΈμ.
- μ κ·Όμ± κ³ λ €νκΈ°: μ₯μ κ° μλ μ¬μ©μκ° μ κ·Όν μ μλλ‘ μ ν리μΌμ΄μ μ μ€κ³νμΈμ. λ체 μ λ ₯ λ°©λ²μ μ 곡νκ³ μ€λμ€ λ° λΉλμ€ μΆλ ₯μ΄ λͺ ννκ³ μ΄ν΄νκΈ° μ¬μ΄μ§ νμΈνμΈμ.
κ²°λ‘
MediaStreamTrack μΈν°νμ΄μ€λ λ―Έλμ΄κ° νλΆν μΉ μ ν리μΌμ΄μ
μ ꡬμΆνκΈ° μν κ°λ ₯ν λꡬμ
λλ€. λ―Έλμ΄ νΈλμ μμ±, μ‘°μ λ° κ΄λ¦¬νλ λ°©λ²μ μ΄ν΄ν¨μΌλ‘μ¨ μ¬μ©μλ₯Ό μν λ§€λ ₯μ μ΄κ³ μνΈμμ©μ μΈ κ²½νμ λ§λ€ μ μμ΅λλ€. μ΄ μ’
ν© κ°μ΄λλ getUserMedia()λ₯Ό μ¬μ©ν νΈλ νλλΆν° μ€λμ€ λ° λΉλμ€ μ²λ¦¬μ κ°μ κ³ κΈ κΈ°μ μ μ΄λ₯΄κΈ°κΉμ§ MediaStreamTrack κ΄λ¦¬μ νμμ μΈ μΈ‘λ©΄μ λ€λ£¨μμ΅λλ€. λ―Έλμ΄ μ€νΈλ¦ΌμΌλ‘ μμ
ν λλ μ¬μ©μ κ°μΈ μ 보 보νΈλ₯Ό μ°μ μνκ³ μ±λ₯μ μ΅μ ννλ κ²μ μμ§ λ§μΈμ. WebRTC λ° κ΄λ ¨ κΈ°μ μ λν μΆκ° νꡬλ μ΄ ν₯λ―Έλ‘μ΄ μΉ κ°λ° λΆμΌμμ μ¬λ¬λΆμ μλμ ν¬κ² ν₯μμν¬ κ²μ
λλ€.